﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using Motorway_Simulator.Components;
using Motorway_Simulator.Simulator;
using System.Runtime.Serialization;

namespace Motorway_Simulator.Components
{
    /// <summary>
    /// An abstract class which all road unit must extend
    /// to inherit properties and methods
    /// </summary>
    [Serializable()]
    public abstract class RoadUnit: GraphicComponent, IComparable<RoadUnit>, ISerializable
    {
        /// <summary>
        /// Indicates if road unit has sign
        /// </summary>
        protected bool hasSign;

        /// <summary>
        /// Sign object
        /// </summary>
        protected Sign sign;

        /// <summary>
        /// Direction Enum: North South East West
        /// </summary>
        public enum Direction {NORTH, SOUTH, EAST, WEST};

        /// <summary>
        /// Direction enum object
        /// </summary>
        protected Direction direction;

        /// <summary>
        /// Holds temporary road unit image
        /// </summary>
        protected Bitmap roadImage;

        /// <summary>
        /// Holds image width and height as float values
        /// </summary>
        protected float imageHeight, imageWidth;

        /// <summary>
        /// Gets or sets the height of the image.
        /// </summary>
        /// <value>The height of the image.</value>
        public float ImageHeight
        {
            get { return imageHeight; }
            set { imageHeight = value; }
        }

        /// <summary>
        /// Gets or sets the width of the image.
        /// </summary>
        /// <value>The width of the image.</value>
        public float ImageWidth
        {
            get { return imageWidth; }
            set { imageWidth = value; }
        }

   


        /// <summary>
        /// Holds actual road unit image
        /// </summary>
        protected Bitmap image;

        /// <summary>
        /// Holds the image zoom
        /// </summary>
        protected float imageZoom;

        /// <summary>
        /// Initializes a new instance of the <see cref="RoadUnit"/> class.
        /// </summary>
        public RoadUnit() 
        {
            // Default settings
            position = new Point();
            hasSign = false;
            direction = Direction.NORTH;
            roadImage = null;
            imageZoom = 1f;
        }

        /// <summary>
        /// Gets or sets the road image.
        /// </summary>
        /// <value>The road image.</value>
        public Bitmap RoadImage
        {
            get { return roadImage; }
            set
            {
                roadImage = value;
                image = roadImage;
            }
        }

        /// <summary>
        /// Must override if unit needs to be painted
        /// </summary>
        /// <param name="g"></param>
        public override void Draw(Graphics g) {        }


        /// <summary>
        /// Gets or sets the position.
        /// </summary>
        /// <value>The position.</value>
        public Point Position
        {
            set { position = value; }
            get { return position; }
        }

        /// <summary>
        /// Direction of the road unit
        /// North, South, East, West
        /// </summary>
        public Direction Direction1
        {
            get { return direction; }
            set { direction = value; }
        }

        /// <summary>
        /// Set if the road unit has a traffic light
        /// </summary>
        public bool RoadSign
        {
            set
            {
                if (value)
                {
                    sign = new Sign(100);
                    hasSign = true;
                }
                else
                {
                    sign = null;
                    hasSign = false;
                }
            }
            get
            {
                return hasSign;
            }
        }

        /// <summary>
        /// Sets the sign speed limit only if sign is initialized
        /// </summary>
        public int SignSpeedLimit
        {
            set
            {
                if (sign != null)
                {
                    sign.SpeedLimit = value;
                }
            }
        }

        /// <summary>
        /// Recalculates the image width and height draws the image
        /// according to the new size
        /// </summary>
        public float ImageZoom
        {
            get { return imageZoom; }
            set 
            { 
                imageZoom = value;
                
                imageHeight = roadImage.Height * imageZoom;
                imageWidth = roadImage.Width * imageZoom;                
            }
        }

        /// <summary>
        /// Rotates the image object clock wise direction
        /// which is determined by road unit direction
        /// </summary>
        /// <param name="images">Image to rotate</param>
        /// <returns>Rotated image</returns>
        public Bitmap RotateImageClockWise(Bitmap images )
        {
            if (direction == Direction.EAST)
            {
              
                images.RotateFlip(RotateFlipType.Rotate90FlipNone);
                float temp = imageHeight;
                imageHeight = imageWidth; 
                imageWidth = temp;

            }
            else if (direction == Direction.SOUTH)
            {
             
                images.RotateFlip(RotateFlipType.Rotate180FlipNone);
            }
            else if (direction == Direction.WEST)
            {
               
                images.RotateFlip(RotateFlipType.Rotate270FlipNone);
                float temp = imageHeight;
                imageHeight = imageWidth;
                imageWidth = temp;
            }
            else if (direction == Direction.NORTH)
            {
                
                
            }
            return images;
        }

        /// <summary>
        /// Gets next position
        /// </summary>
        /// <param name="position">Position</param>
        /// <param name="speed">Speed</param>
        /// <returns></returns>
        public abstract Point getNextPosition(Point position, int speed);


        #region IComparable<RoadUnit> Members

        /// <summary>
        /// Compares the current object with another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following meanings: Value Meaning Less than zero This object is less than the <paramref name="other"/> parameter.Zero This object is equal to <paramref name="other"/>. Greater than zero This object is greater than <paramref name="other"/>.
        /// </returns>
        public int CompareTo(RoadUnit other)
        {
            switch (this.Direction1)
            {
                case RoadUnit.Direction.NORTH:
                {
                   return this.Position.Y - other.Position.Y;
                }
                case RoadUnit.Direction.SOUTH:
                {
                    return other.Position.Y -this.Position.Y ;
                }
                case RoadUnit.Direction.EAST:
                {
                    return other.Position.X - this.Position.X;
                }
                case RoadUnit.Direction.WEST:
                {
                    return Position.X - other.Position.X;
                }
                default:
                    return 0;
            }
        }

        #endregion

        #region ISerializable Members
        /// <summary>
        /// Add value for serialization
        /// </summary>
        /// <param name="info"></param>
        /// <param name="context"></param>
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Position", position);
            info.AddValue("Image", image);
            info.AddValue("ImageWidth", imageWidth);
            info.AddValue("ImageHeight", imageHeight);
            info.AddValue("RoadImage", roadImage);
            info.AddValue("ImageZoom", imageZoom);
            info.AddValue("Sign", sign);
            info.AddValue("HasSign", hasSign);
            info.AddValue("Direction", direction);

        }

        #endregion

        /// <summary>
        /// Is called to set values read from file
        /// </summary>
        /// <param name="info">Info</param>
        /// <param name="ctxt">ctxt</param>
        public RoadUnit(SerializationInfo info, StreamingContext ctxt)
        {
            position = (Point)info.GetValue("Position", typeof(Point));
            image = (Bitmap)info.GetValue("Image", typeof(Bitmap));
            imageWidth = (float)info.GetValue("ImageWidth", typeof(float));
            imageHeight = (float)info.GetValue("ImageHeight", typeof(float));
            roadImage = (Bitmap)info.GetValue("RoadImage", typeof(Bitmap));
            imageZoom = (float)info.GetValue("ImageZoom", typeof(float));
            sign = (Sign)info.GetValue("Sign", typeof(Sign));
            hasSign = (bool)info.GetValue("HasSign", typeof(bool));
            direction = (Direction)info.GetValue("Direction", typeof(Direction));
            //Get the values from info and assign them to the appropriate properties

            
        }



        /// <summary>
        /// Determines whether [is not in current road unit] [the specified vehicle].
        /// </summary>
        /// <param name="vehicle">The vehicle.</param>
        /// <returns>
        /// 	<c>true</c> if [is not in current road unit] [the specified vehicle]; otherwise, <c>false</c>.
        /// </returns>
        public virtual bool IsNotInCurrentRoadUnit(Vehicle vehicle)
        {
            switch (this.Direction1)
            {
                case Direction.NORTH:
                    if (Position.Y <= vehicle.CurrentPoint.Y)
                    {
                        return true;
                    }
                    break;

                case Direction.SOUTH:
                    if (Position.Y+ ImageHeight >= vehicle.CurrentPoint.Y)
                    {
                        return true;
                    }
                    break;
                case Direction.WEST:

                    if (Position.X <= vehicle.CurrentPoint.X)
                    {
                        return true;
                    }
                    break;
                case Direction.EAST:
                    if (Position.X + ImageWidth >= vehicle.CurrentPoint.X)
                    {
                        return true;
                    }
                    break;
                    
            }
            return false;

        }
    }

}